// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Data
{
    internal class ModelDeleteBuilder : ISqlExpressionContext
    {
        private readonly DataContext _dataContext;
        private readonly ModelSqlHelper _modelSqlHelper;
        private readonly SqlExpressionBuilder _sqlExpressionBuilder;
        private readonly ModelConditionBuilder _conditionBuilder;
        private readonly ModelTableBuilder _tableBuilder;
        private readonly ISqlDeleteBuilder _sqlDeleteBuilder;

        private readonly IDictionary<Type, ModelQueryBuilder> _embeddedBuilders;

        public ModelDeleteBuilder(DataContext dataContext, Type modelType)
        {
            _dataContext = dataContext;

            _modelSqlHelper = new ModelSqlHelper(modelType);
            _sqlExpressionBuilder = new SqlExpressionBuilder(this);

            _conditionBuilder = new ModelConditionBuilder(_modelSqlHelper, _sqlExpressionBuilder);
            _tableBuilder = new ModelTableBuilder(_modelSqlHelper, _sqlExpressionBuilder);

            _sqlDeleteBuilder = SqlBuilderFactory.Create<ISqlDeleteBuilder>();

            _embeddedBuilders = new Dictionary<Type, ModelQueryBuilder>();
        }

        public PocoModelMeta ModelMeta => _modelSqlHelper.ModelMeta;

        public ISqlDeleteBuilder GetDeleteBuilder()
        {
            _sqlDeleteBuilder.Reset();
            this.BuildParameters();

            if (this.ModelMeta.TableDefined || this.ModelMeta.Tables.Count == 0)
            {
                _sqlDeleteBuilder.Delete(_modelSqlHelper.GetMappedTableAliasOrName());
            }

            if (this.ModelMeta.HasFromTable)
            {
                var tables = this.ModelMeta.Tables.Where(o => o is IFromTable || o is IJoinTable)
                .ToList();

                _tableBuilder.BuildFromAndJoin(_sqlDeleteBuilder, tables);
            }

            _conditionBuilder.BuildWhere(_sqlDeleteBuilder);

            return _sqlDeleteBuilder;
        }

        public ISqlDeleteBuilder GetDeleteByKeyBuilder()
        {
            _sqlDeleteBuilder.Reset();

            var table = _modelSqlHelper.GetMappedTableFullName();

            _sqlDeleteBuilder.Delete(table);

            _modelSqlHelper.BuildWhereForKeys(_sqlDeleteBuilder,
                                _modelSqlHelper.ModelMeta.UpdatableProperties);

            return _sqlDeleteBuilder;
        }

        private void BuildParameters()
        {
            var guid = Guid.NewGuid();

            foreach (var parameter in _modelSqlHelper.ModelMeta.SqlParameters)
            {
                ((SqlBuilderBase)_sqlDeleteBuilder).AddParameters(
                    SqlBuilder.Parameter(parameter.DataType, parameter.Name, guid.ToString()));
            }
        }

        public string BuildEmbeddedModel(Type modelType, string selectName)
        {
            if (!_embeddedBuilders.TryGetValue(modelType, out var builder))
            {
                builder = new ModelQueryBuilder(_dataContext, modelType);
                builder.BuildModelSelect(selectName);

                _embeddedBuilders.Add(modelType, builder);
            }

            _sqlDeleteBuilder.AddParameters(((SqlBuilderBase)builder.SqlQueryBuilder).BuilderParameters);

            return builder.SqlQueryBuilder.ToSqlString(_dataContext);
        }

        public string BuildPropertyRef(string expression)
        {
            return _modelSqlHelper.BuildPropertyRef(expression);
        }

        public virtual string GetParameterPlacehold(string parameterId)
        {
            return _sqlDeleteBuilder.GetParameterPlacehold(parameterId);
        }
    }
}
